package com.bosssoft.platform.fasttcc.support.spring;

import com.bosssoft.platform.fasttcc.RemoteResourceFactory;
import com.bosssoft.platform.fasttcc.TccLogger;
import com.bosssoft.platform.fasttcc.TccTransactionManager;
import com.bosssoft.platform.fasttcc.assist.TccDataSource;
import com.bosssoft.platform.fasttcc.assist.TccMethodIntercepter;
import com.bosssoft.platform.fasttcc.impl.DefaultTccConfiguration;
import com.bosssoft.platform.fasttcc.log.file.FileTccLogger;
import com.bosssoft.platform.fasttcc.rpc.command.DefaultTccCommandHandler;
import com.bosssoft.platform.fasttcc.rpc.command.TccCommandHandler;
import com.bosssoft.platform.fasttcc.rpc.filter.RpcFilter;
import com.bosssoft.platform.fasttcc.rpc.filter.impl.DefaultRpcFilter;
import com.bosssoft.platform.fasttcc.support.BeanClassFinder;
import com.bosssoft.platform.fasttcc.support.CompleteStageHelper;
import com.bosssoft.platform.fasttcc.support.OkHttpRemoteResourceFactory;
import com.jfireframework.baseutil.StringUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.context.annotation.Primary;

import javax.sql.DataSource;
import java.io.File;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadFactory;

@Configuration
@Import({SpringBeanClassFinder.class, SpringCompleteStageHelper.class, TccMethodEnHancer.class, TccController.class, TccHandlerFilter.class})
public class SpringTccConfiguration implements ApplicationContextAware
{
    @Value("${tcc.basedir:}")
    private              String             baseDir;
    @Value("${tcc.identifier:}")
    private              String             identifier;
    @Value("${spring.application.name:}")
    private              String             applicationName;
    @Value("${server.port:}")
    private              String             port;
    private              ApplicationContext applicationContext;
    private static final Logger             LOGGER = LoggerFactory.getLogger(SpringTccConfiguration.class);

    @Bean
    @Primary
    @ConditionalOnClass(name = "okhttp3.OkHttpClient")
    public RemoteResourceFactory okHttpfactory()
    {
        return new OkHttpRemoteResourceFactory(identifier);
    }

    @Primary
    @Bean
    public DataSource dataSource(DataSource dataSource, TccTransactionManager tccTransactionManager)
    {
        TccDataSource tccDataSource = new TccDataSource();
        tccDataSource.setPhysicalDs(dataSource);
        tccDataSource.setTccTransactionManager(tccTransactionManager);
        return tccDataSource;
    }

    @Bean
    public TccMethodIntercepter tccMethodIntercepter(TccTransactionManager tccTransactionManager)
    {
        TccMethodIntercepter tccMethodIntercepter = new TccMethodIntercepter();
        tccMethodIntercepter.setTccTransactionManager(tccTransactionManager);
        tccMethodIntercepter.setJdbcTransactionInfoDetect(new SpringJdbcTransactionInfoDetect());
        return tccMethodIntercepter;
    }

    @Bean
    public TccCommandHandler tccCommandHandler()
    {
        return new DefaultTccCommandHandler();
    }

    @Bean
    public TccTransactionManager tccTransactionManager( TccLogger tccLogger, RemoteResourceFactory remoteResourceFactory, TccCommandHandler tccCommandHandler, BeanClassFinder beanClassFinder, CompleteStageHelper completeStageHelper, @Qualifier("realDataSource") DataSource dataSource)
    {
        if (StringUtil.isNotBlank(baseDir) == false)
        {
            baseDir = new File("").getAbsolutePath();
        }
        if (StringUtil.isNotBlank(identifier) == false)
        {
            String hostAddress;
            try
            {
                hostAddress = InetAddress.getLocalHost().getHostAddress();
            }
            catch (UnknownHostException e)
            {
                hostAddress = "0.0.0.0";
            }
            port = StringUtil.isNotBlank(port) ? port : "0";
            applicationName = StringUtil.isNotBlank(applicationName) ? applicationName : "jfireTcc_Application";
            identifier = hostAddress + ":" + port + ":" + applicationName;
        }
        DefaultTccConfiguration configuration = new DefaultTccConfiguration();
        ExecutorService executorService = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors() * 4, new ThreadFactory()
        {
            int count;

            @Override
            public synchronized Thread newThread(Runnable r)
            {
                return new Thread(r, "Tcc-CompletePool-" + (count++));
            }
        });
        LOGGER.debug("物理数据源:{}", dataSource.getClass());
        configuration.setExecutorService(executorService);
        configuration.setDataSource(dataSource);
        configuration.setBeanClassFinder(beanClassFinder);
        configuration.setCompleteStageHelper(completeStageHelper);
        configuration.setIdentifier(identifier);
        configuration.setLogBaseDir(baseDir);
        configuration.setRemoteResourceFactory(remoteResourceFactory);
        configuration.setTccCommandHandler(tccCommandHandler);
        configuration.setTccLogger(tccLogger);
        TccTransactionManager transactionManager = configuration.build();
        return transactionManager;
    }

    @Bean
    public TccLogger defaultTccLogger(@Qualifier("realDataSource") DataSource dataSource)
    {
        return new FileTccLogger(baseDir, dataSource);
    }


    @Bean
    public RpcFilter rpcFilter(TccTransactionManager tccTransactionManager)
    {
        DefaultRpcFilter filter = new DefaultRpcFilter();
        filter.setTransactionManager(tccTransactionManager);
        return filter;
    }

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException
    {
        this.applicationContext = applicationContext;
    }

    public String getBaseDir()
    {
        return baseDir;
    }

    public void setBaseDir(String baseDir)
    {
        this.baseDir = baseDir;
    }

    public String getIdentifier()
    {
        return identifier;
    }

    public void setIdentifier(String identifier)
    {
        this.identifier = identifier;
    }

    public ApplicationContext getApplicationContext()
    {
        return applicationContext;
    }
}
